#include "../server.h"

#define PUBKEY_ID_SIZE          4

static OSSL_KEY prikey;

struct _pubkey_node
{/*{{{*/
    char key_id[PUBKEY_ID_SIZE+1];
    OSSL_KEY key;
    struct rb_link link;
};/*}}}*/

static pthread_rwlock_t pubkey_lock;
static struct rb_tree pubkey_tree;

int sso_init()
{/*{{{*/
    SVCNODE sn;
    char respond[100];
    char prikey_file[LONG_FILE_NM_SIZE+1];
    char passwd[50];
    char *p;
    char *curr;
    char *rtn;
    int res;

    /*--- 私钥 ---*/
    res=sn_create(cnfg_info._key_.sn_passwd_addr, SN_MODE_ONESHOT, &sn);
    if(res)
    {
        fprintf(stderr,
                "%s: svcnode init for sn_passwd fail, errcode:%d\n",
                __func__, res);
        return(-1);
    }
    res=sn_call(sn, "GETPRIKEYPASSWD", NULL, respond);
    if(res)
    {
        fprintf(stderr,
                "%s: call to sn_passwd fail, errcode:%d\n",
                __func__, res);
        return(-1);
    }
    curr=respond;
    rtn=para_get_item(respond, &curr);
    if(!rtn || strcmp(rtn, SOAR_ERR_OK))
    {
        fprintf(stderr, "%s: get private key passwd fail, errcode:%s\n",
                __func__, rtn?rtn:"null");
        return(-1);
    }
    p=para_get_item(respond, &curr);
    if(!p)
    {
        fprintf(stderr, "%s: private key passwd invalid\n", __func__);
        return(-1);
    }
    strcpy(passwd, p);
    //printf("passwd: %s\n", dbpasswd);
    sn_free(sn);

    sprintf(prikey_file, "%s/private.pem", cnfg_info._key_.location);
    res=ossl_rsa_load_private_key_file(prikey_file, passwd, &prikey);
    if(res)
    {
        errlog("%s: load private key(%s) fail, res:%d",
                __func__, prikey_file, res);
        return(-1);
    }

    /*--- 公钥 ---*/
    pthread_rwlock_init(&pubkey_lock, NULL);
    rb_create(TREE_KEY_STRING, &pubkey_tree);

    return(0);
}/*}}}*/

int reload_pubkey(char *pubkey_id)
{/*{{{*/
    struct rb_link *link;

    pthread_rwlock_wrlock(&pubkey_lock);

    link=rb_find(&pubkey_tree, (void *)pubkey_id);
    if(link)
    {
        if(rb_delete_link(&pubkey_tree, link))
        {
            struct _pubkey_node *node=
                tree_entry(link, struct _pubkey_node, link);
            free(node);
            if(cnfg_info._errlog_.tracking)
                errlog("%s: public key(%s) deleted from tree",
                        __func__, pubkey_id);
        }
        else
        {
            errlog("%s: rb_delete_link(%s) fail, impossible",
                    __func__, pubkey_id);
        }

    }
    else
    {
        if(cnfg_info._errlog_.tracking)
            errlog("%s: public key(%s) not in tree",
                    __func__, pubkey_id);
    }

    pthread_rwlock_unlock(&pubkey_lock);

    return(0);
}/*}}}*/

OSSL_KEY get_pubkey(char *pubkey_id)
{/*{{{*/
    char pubkey_file[LONG_FILE_NM_SIZE+1];
    struct rb_link *p;
    struct _pubkey_node *node;
    int res;

	// 查找
    p=rb_find(&pubkey_tree, (void *)pubkey_id);
    if(p)
    {
        node=tree_entry(p, struct _pubkey_node, link);
        return(node->key);
    }
    pthread_rwlock_unlock(&pubkey_lock);

	// 载入
    pthread_rwlock_wrlock(&pubkey_lock);

    if(cnfg_info._errlog_.tracking)
        errlog("%s : pubkey_id(%s) not in tree, load",
            __func__, pubkey_id);
    p=rb_find(&pubkey_tree, (void *)pubkey_id);
    if(p)
    {
        errlog("%s: load pubkey_id(%s) but it is in tree, rare",
             __func__, pubkey_id);
        node=tree_entry(p, struct _pubkey_node, link);
        return(node->key);
    }
    ALLOC_DOERR(node, struct _pubkey_node,
            errlog("%s: alloc pubkey node fail", __func__);
            return(NULL));
   	strcpy(node->key_id, pubkey_id);
    sprintf(pubkey_file, "%s/%s", cnfg_info._key_.location, pubkey_id);
   	res=ossl_rsa_load_public_key_file(pubkey_file, &node->key);
   	if(res)
    {
        errlog("%s : ossl_rsa_load_public_key_file(%s) fail, res:%d",
                __func__, pubkey_file, res);
        free(node);
        return(NULL);
    }
    tree_setkey_string(node, key_id, link);
    if(rb_insert(&pubkey_tree, &node->link))
    {
        errlog("%s: insert pubkey_id(%s) to tree, exist, impossible",
               __func__, pubkey_id);
    }
    if(cnfg_info._errlog_.tracking)
        errlog("%s: load pubkey_id(%s) to tree", __func__, pubkey_id);

    return(node->key);
}/*}}}*/

SVC_FUNC(GETTS)
{/*{{{*/
    char buf[20];

    sprintf(buf, "%ld", (long)curr_timestamp(NULL));

    para_put_item(respond, SOAR_ERR_OK);
    para_put_item(respond, buf);
    return(0);
}/*}}}*/

SVC_FUNC(DIFFTS)
{/*{{{*/
    char para_desc[50];
    PARA para;
	long verify_ts;
    char *p;
    int res;

    sprintf(para_desc, "verify_ts(string:1-20)");
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: msg(%s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_ref_by_name(para, "verify_ts", &p);
    verify_ts=atoi(p);
    para_free(para);

    if(verify_ts <= 0)
    {
        errlog("%s: invalid timestamp:%s", __func__, p);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
	}

    para_put_item(respond, SOAR_ERR_OK);
    para_put_item_int(respond, (int)(curr_timestamp(NULL)-verify_ts));
    return(0);
}/*}}}*/

SVC_FUNC(ENCRYPT)
{/*{{{*/
    int rtn;
    char para_desc[100];
    PARA para;
    char *pubkey_id, *plain_text;
    OSSL_KEY pubkey;
    int enc_size;
    int code_size;
    int res;

    sprintf(para_desc, "pubkey_id(number:%d) plain_text(string:1-%d)",
            PUBKEY_ID_SIZE, SN_SECURE_PLAIN_MAX_SIZE);
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: request(%.20s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_ref_by_name(para, "pubkey_id", &pubkey_id);
    para_ref_by_name(para, "plain_text", &plain_text);

    pthread_rwlock_rdlock(&pubkey_lock);    // read lock

    pubkey=get_pubkey(pubkey_id);
    if(!pubkey)
    {
        errlog("%s: get_pubkey(%s) fail", __func__, pubkey_id);
        para_put_item(respond, SOAR_ERR_SECURE_NOKEY);
        rtn=-1; goto GO_OUT;
    }
    enc_size=SN_SECURE_ENC_MAX_SIZE;
    res=ossl_rsa_encrypt(pubkey,
            (unsigned char *)plain_text, strlen(plain_text),
            (unsigned char *)output, &enc_size);
    if(res)
    {
        errlog("%s: ossl_rsa_encrypt fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_ENCRYPT);
        rtn=-1; goto GO_OUT;
    }
    //expand_to_str((unsigned char *)output, enc_size, output_s);
    res=ossl_encode(OSSL_ENCODE_BASE64,
            output, enc_size,
            output_s, &code_size);
    if(res)
    {
        errlog("%s: ossl_encode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_ENCODE);
        rtn=-1; goto GO_OUT;
    }
    base64_unwrap(output_s, output_s);
    para_put_item(respond, SOAR_ERR_OK);
    para_put_item(respond, output_s);
    rtn=0;

GO_OUT:
    pthread_rwlock_unlock(&pubkey_lock);    // unlock
    para_free(para);
    return(rtn);
}/*}}}*/

SVC_FUNC(DECRYPT)
{/*{{{*/
    int rtn;
    char para_desc[100];
    PARA para;
    int enc_size;
    int plain_size;
    int len;
	int res;

    sprintf(para_desc, "enc_text(string:1-%d)", SN_SECURE_ENC_MAX_SIZE*2);
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: request(%.20s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_copy_by_name(para, "enc_text", output_s);
    para_free(para);

    //enc_size=strlen(enc_text)/2;
    //collapse_from_str(enc_text, enc_size*2, output);
    base64_wrap(output_s, output_s);
    res=ossl_decode(OSSL_ENCODE_BASE64,
            output_s, strlen(output_s),
            output, &enc_size);
    if(res)
    {
        errlog("%s: ossl_decode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_DECODE);
        rtn=-1; goto GO_OUT;
    }
    plain_size=SN_SECURE_ENC_MAX_SIZE*2;
    res=ossl_rsa_decrypt(prikey, output, enc_size,
            (unsigned char *)output_s, &plain_size);
    if(res)
    {   
        errlog("%s: ossl_rsa_decrypt fail, res:%d", __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_DECRYPT);
        rtn=-1; goto GO_OUT;
    }
    output_s[plain_size]=ZERO;
	len=strlen(output_s);
	if(len != plain_size)
    {
        errlog("%s: output_s(%d) != plain_size(%d)",
                __func__, len, plain_size);
        para_put_item(respond, SOAR_ERR_SECURE_DECRYPT);
        rtn=-1; goto GO_OUT;
    }
    para_put_item(respond, SOAR_ERR_OK);
    para_put_item(respond, output_s);
    rtn=0;

GO_OUT:
    return(rtn);
}/*}}}*/

SVC_FUNC(SIGN)
{/*{{{*/
    int rtn;
    char para_desc[100];
    PARA para;
	char *plain_text;
    int sign_size;
    int code_size;
	int res;

    sprintf(para_desc, "plain_text(string:1-%d)",
            SN_SECURE_PLAIN_MAX_SIZE);
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: request(%.20s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_ref_by_name(para, "plain_text", &plain_text);

	res=ossl_rsa_sign(prikey,
            (unsigned char *)plain_text, strlen(plain_text),
            output, &sign_size);
	if(res)
    {
        errlog("%s: ossl_rsa_sign fail, res:%d", __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_SIGN);
        rtn=-1; goto GO_OUT;
    }
    //expand_to_str(output, sign_size, output_s);
    res=ossl_encode(OSSL_ENCODE_BASE64,
            output, sign_size,
            output_s, &code_size);
    if(res)
    {
        errlog("%s: ossl_encode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_ENCODE);
        rtn=-1; goto GO_OUT;
    }
    base64_unwrap(output_s, output_s);
    para_put_item(respond, SOAR_ERR_OK);
    para_put_item(respond, output_s);
    rtn=0;

GO_OUT:
    para_free(para);
    return(rtn);
}/*}}}*/

SVC_FUNC(VERIFY)
{/*{{{*/
    int rtn;
    char para_desc[100];
    PARA para;
	char *pubkey_id, *plain_text, *sign_text;
    OSSL_KEY pubkey;
    int sign_size;
    int res;

    sprintf(para_desc,
            "pubkey_id(number:%d)"
            "plain_text(string:1-%d)"
            "sign_text(string:1-%d)",
            PUBKEY_ID_SIZE,
            SN_SECURE_PLAIN_MAX_SIZE,
            SN_SECURE_SIGN_MAX_SIZE);
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: request(%.20s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_ref_by_name(para, "pubkey_id", &pubkey_id);
    para_ref_by_name(para, "plain_text", &plain_text);
    para_ref_by_name(para, "sign_text", &sign_text);

    pthread_rwlock_rdlock(&pubkey_lock);    // read lock

    pubkey=get_pubkey(pubkey_id);
    if(!pubkey)
    {
        errlog("%s: get_pubkey(%s) fail", __func__, pubkey_id);
        para_put_item(respond, SOAR_ERR_SECURE_NOKEY);
        rtn=-1; goto GO_OUT;
    }
    //sign_size=strlen(sign_text)/2;
    //collapse_from_str(sign_text, sign_size*2, output);
    base64_wrap(sign_text, output_s);
    res=ossl_decode(OSSL_ENCODE_BASE64,
            output_s, strlen(output_s),
            output, &sign_size);
    if(res)
    {
        errlog("%s: ossl_decode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_DECODE);
        rtn=-1; goto GO_OUT;
    }
    res=ossl_rsa_verify(pubkey,
            (unsigned char *)plain_text, strlen(plain_text),
            output, sign_size);
    if(res)
    {
		errlog("%s: ossl_rsa_verify(%s) fail, res:%d, plain:%s, sign:%s",
            __func__, pubkey_id, res, plain_text, sign_text);
        para_put_item(respond, SOAR_ERR_SECURE_VERIFY);
        rtn=-1; goto GO_OUT;
    }
	para_put_item(respond, SOAR_ERR_OK);
    rtn=0;

GO_OUT:
    pthread_rwlock_unlock(&pubkey_lock);    // unlock
    para_free(para);
    return(rtn);
}/*}}}*/

SVC_FUNC(ENCSIGN)
{/*{{{*/
    int rtn;
    char para_desc[100];
    PARA para;
	char *pubkey_id, *plain_text;
    OSSL_KEY pubkey;
    int enc_size;
    int sign_size;
    int code_size;
    int res;

    sprintf(para_desc,
            "pubkey_id(number:%d)"
            "plain_text(string:1-%d)",
            PUBKEY_ID_SIZE,
            SN_SECURE_PLAIN_MAX_SIZE);
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: request(%.20s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_ref_by_name(para, "pubkey_id", &pubkey_id);
    para_ref_by_name(para, "plain_text", &plain_text);

    pthread_rwlock_rdlock(&pubkey_lock);    // read lock

    // 加密
    pubkey=get_pubkey(pubkey_id);
    if(!pubkey)
    {
        errlog("%s: get_pubkey(%s) fail", __func__, pubkey_id);
        para_put_item(respond, SOAR_ERR_SECURE_NOKEY);
        rtn=-1; goto GO_OUT;
    }
    enc_size=SN_SECURE_ENC_MAX_SIZE;
    res=ossl_rsa_encrypt(pubkey,
            (unsigned char *)plain_text, strlen(plain_text),
            output, &enc_size);
    if(res)
    {
        errlog("%s: ossl_rsa_encrypt fail, res:%d", __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_ENCRYPT);
        rtn=-1; goto GO_OUT;
    }
    //expand_to_str(output, enc_size, output_s);
    res=ossl_encode(OSSL_ENCODE_BASE64,
            output, enc_size,
            output_s, &code_size);
    if(res)
    {
        errlog("%s: ossl_encode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_ENCODE);
        rtn=-1; goto GO_OUT;
    }
    base64_unwrap(output_s, output_s);
    para_put_item(respond, output_s);

    // 签名
    res=ossl_rsa_sign(prikey,
            (unsigned char *)output_s, strlen(output_s),
            output, &sign_size);
    if(res)
    {
		errlog("%s: ossl_rsa_sign fail, res:%d", __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_SIGN);
        rtn=-1; goto GO_OUT;
    }
    //expand_to_str(output, sign_size, output_s);
    res=ossl_encode(OSSL_ENCODE_BASE64,
            output, sign_size,
            output_s, &code_size);
    if(res)
    {
        errlog("%s: ossl_encode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_ENCODE);
        rtn=-1; goto GO_OUT;
    }
    base64_unwrap(output_s, output_s);
    para_put_item(respond, output_s);

    para_insert_item(respond, SOAR_ERR_OK);
    rtn=0;

GO_OUT:
    pthread_rwlock_unlock(&pubkey_lock);    // unlock
    para_free(para);
    return(rtn);
}/*}}}*/

SVC_FUNC(VRFYDEC)
{/*{{{*/
    int rtn;
    char para_desc[100];
    PARA para;
	char *pubkey_id, *enc_text, *sign_text;
    OSSL_KEY pubkey;
    int sign_size;
    int enc_size;
    int plain_size;
	int len;
    int res;

    sprintf(para_desc,
            "pubkey_id(number:%d)"
            "enc_text(string:1-%d)"
            "sign_text(string:1-%d)",
            PUBKEY_ID_SIZE,
            SN_SECURE_ENC_MAX_SIZE*2,
            SN_SECURE_SIGN_MAX_SIZE*2);
    res=para_load(request, para_desc, &para);
    if(res)
    {
        errlog("%s: request(%.20s) no.%d para parse fail",
                __func__, request, res);
        para_put_item(respond, SOAR_ERR_PARA);
        return(-1);
    }
    para_ref_by_name(para, "pubkey_id", &pubkey_id);
    para_ref_by_name(para, "enc_text", &enc_text);
    para_ref_by_name(para, "sign_text", &sign_text);

    pthread_rwlock_rdlock(&pubkey_lock);    // read lock

    // 校验
    pubkey=get_pubkey(pubkey_id);
    if(!pubkey)
    {
        errlog("%s: get_pubkey(%s) fail", __func__, pubkey_id);
        para_put_item(respond, SOAR_ERR_SECURE_NOKEY);
        rtn=-1; goto GO_OUT;
    }
    //enc_size=strlen(enc_text)/2;
	//sign_size=strlen(sign_text)/2;
    //collapse_from_str(sign_text, sign_size*2, output);
    base64_wrap(sign_text, output_s);
    res=ossl_decode(OSSL_ENCODE_BASE64,
            output_s, strlen(output_s),
            output, &sign_size);
    if(res)
    {
        errlog("%s: ossl_decode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_DECODE);
        rtn=-1; goto GO_OUT;
    }
    res=ossl_rsa_verify(pubkey,
            (unsigned char *)enc_text, strlen(enc_text),
            output, sign_size);
    if(res)
    {
		errlog("%s: ossl_rsa_verify(%s) fail, res:%d, "
                "enc:%.20s, sign:%.20s",
                __func__, pubkey_id, res, enc_text, sign_text);
        para_put_item(respond, SOAR_ERR_SECURE_VERIFY);
        rtn=-1; goto GO_OUT;
    }

    // 解密
    //collapse_from_str(enc_text, enc_size*2, output);
    base64_wrap(enc_text, output_s);
    res=ossl_decode(OSSL_ENCODE_BASE64,
            output_s, strlen(output_s),
            output, &enc_size);
    if(res)
    {
        errlog("%s: ossl_decode fail, res:%d",
                __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_DECODE);
        rtn=-1; goto GO_OUT;
    }
    plain_size=SN_SECURE_ENC_MAX_SIZE*2;
    res=ossl_rsa_decrypt(prikey,
            output, enc_size,
            (unsigned char *)output_s, &plain_size);
    if(res)
    {
        errlog("%s: ossl_rsa_decrypt fail, res:%d", __func__, res);
        para_put_item(respond, SOAR_ERR_SECURE_DECRYPT);
        rtn=-1; goto GO_OUT;
    }
    output_s[plain_size]=ZERO;
    len=strlen(output_s);
    if(len != plain_size)
    {
        errlog("%s: dec(%d) != plain(%d)", __func__, len, plain_size);
        para_put_item(respond, SOAR_ERR_SECURE_DECRYPT);
        rtn=-1; goto GO_OUT;
    }

    para_put_item(respond, SOAR_ERR_OK);
    para_put_item(respond, output_s);
    rtn=0;

GO_OUT:
    pthread_rwlock_unlock(&pubkey_lock);    // unlock
    para_free(para);
    return(rtn);
}/*}}}*/
